package com.semidata.trp.decision.bid;

import java.net.URLDecoder;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import com.semidata.rtd.core.log.Logger;
import com.semidata.rtd.core.service.SendableService;
import com.semidata.rtd.engine.dynamic.DynamicClassLoader;
import com.semidata.rtd.engine.dynamic.DynamicCompiler;

public class CompileService implements SendableService<CompileEvent>{
	private LinkedBlockingQueue<CompileEvent> queue = new LinkedBlockingQueue<>();
	private AtomicReference<Map<Integer,Class<?>>> algorithm = null;
	private Set<Integer> ids = new HashSet<>();
	private volatile boolean quit = false;
	private Thread thread = null;
	public CompileService() {
		algorithm = new AtomicReference<>();
		Map<Integer,Class<?>> algos = new HashMap<>();
		this.algorithm.set(algos);
	}
	
	@Override
	public void start() {
		// TODO Auto-generated method stub
		thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				
					while (true) {
						try {
							CompileEvent event = queue.poll(100, TimeUnit.MILLISECONDS);
							if (event == null) {
								if (quit) {
									break;
								} else {
									continue;
								}
							}
							int id = event.getAlgorithmId();
							if (!ids.contains(id)) {
								String code = event.getCode();
								Class<?> algorithmClass = null;
								try {
									DynamicClassLoader loader = new DynamicClassLoader();
									String newClassName = "Algorithm" + id;
									code = code.replace("Algorithm", newClassName);
									code = URLDecoder.decode(code);
									algorithmClass = DynamicCompiler
									        .compile(loader, "com.semidata.trp.algorithm." + newClassName, code.toString());
								} catch (Exception e) {
									// TODO Auto-generated catch block
									Logger.log("ERROR", "Compile algorithm : " + id + " fail");
									Logger.log("ERROR", e);
									e.printStackTrace();
									continue;
								}
								if (algorithmClass != null) {
									Map<Integer,Class<?>> algos = algorithm.get();
									algos.put(id, algorithmClass);
								}
								ids.add(id);
							}
							
						} catch (Exception e) {
							Logger.log("ERROR", e);
							e.printStackTrace();
						}
						
					}
			}
		});
		thread.start();
		
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		quit = true;
		try {
			if (thread != null) {
				thread.join();
			}
		} catch (Exception e) {
		}
		
	}

	@Override
	public void send(CompileEvent event) {
		// TODO Auto-generated method stub
		try {
			queue.put(event);
		} catch (Exception e) {
			e.printStackTrace();
			
		}
	}
	
	public Map<Integer,Class<?>> getAlgos() {
		return this.algorithm.get();
	}

}
